﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Asol.Reporting.Support;
using MainParser = Asol.Reporting.Support.Parser;

namespace Asol.Reporting.Editor.Expression
{
    #region class Evaluator : třída, která dostane výraz jako string, zkompiluje jej do funkce a umožní ji vyhodnotit v kontextu proměnných
    /// <summary>
    /// Evaluator je třída, která dostane výraz jako string, zkompiluje jej do funkce,
    /// a v daném kontextu (který slouží k vracení hodnot proměnných) danou funkci vyhodnotí a vrátí její výslednou hodnotu.
    /// </summary>
    public class Evaluator
    {
        #region Public static metody pro vyhodnocení výrazu, pro vyhledání Pluginu funkce
        /// <summary>
        /// Metoda vrátí hodnotu daného výrazu, s využitím daného kontextu pro vyhodnocení proměnných.
        /// </summary>
        /// <param name="expression"></param>
        /// <param name="context"></param>
        /// <returns></returns>
        public static EValue Evaluate(string expression, IEvaluateContext context)
        {
            if (expression == null) return EValue.Empty;
            CompiledFormula formula = Current._GetFormula(expression);
            return formula.Evaluate(context);
        }
        /// <summary>
        /// Metoda najde a vrátí objekt (Plugin), který má vyhodnocovat metodu uvedeného jména.
        /// </summary>
        /// <param name="functionName"></param>
        /// <returns></returns>
        public static FunctionImplement FindFunction(string functionName)
        {
            if (String.IsNullOrEmpty(functionName)) return null;
            FunctionImplement function = Current._FindFunction(functionName);
            return function;
        }
        /// <summary>
        /// NumberFormatInfo
        /// </summary>
        public static System.Globalization.NumberFormatInfo NumberFormatter { get { return Current._NumberFormatter; } }
        /// <summary>
        /// DateTimeFormatInfo
        /// </summary>
        public static System.Globalization.DateTimeFormatInfo DateTimeFormatter { get { return Current._DateTimeFormatter; } }
        /// <summary>
        /// Obsahuje (vrací) seznam všech známých funkcí. Seznam je setříděn.
        /// Obsahuje plné názvy = původní jméno funkce + závorky + popisky parametrů.
        /// </summary>
        public static List<FunctionImplement> FunctionImplementList
        {
            get
            {
                if (Current._FunctionDict == null)
                    Current._FunctionDict = _CreateFunctionDict();
                List<FunctionImplement> implements = new List<FunctionImplement>(Current._FunctionDict.Values);
                implements.Sort((a, b) => a.FullName.CompareTo(b.FullName));
                return implements;
            }
        }
        /// <summary>
        /// Obsahuje (vrací) seznam všech známých funkcí. Seznam je setříděn.
        /// Obsahuje plné názvy = původní jméno funkce + závorky + popisky parametrů.
        /// </summary>
        public static List<string> FunctionFullNameList
        {
            get
            {
                if (Current._FunctionDict == null)
                    Current._FunctionDict = _CreateFunctionDict();
                List<FunctionImplement> implements = new List<FunctionImplement>(Current._FunctionDict.Values);
                List<string> functions = implements.ConvertAll<string>(i => i.FullName);
                functions.Sort();
                return functions;
            }
        }
        /// <summary>
        /// Obsahuje (vrací) seznam všech známých názvů funkcí. Seznam je setříděn.
        /// Obsahuje klíče = holé jméno funkce, v případě Case-Insensitive je ToLower().
        /// </summary>
        public static List<string> FunctionKeyList
        {
            get
            {
                if (Current._FunctionDict == null)
                    Current._FunctionDict = _CreateFunctionDict();
                List<string> functions = new List<string>(Current._FunctionDict.Keys);
                functions.Sort();
                return functions;
            }
        }
        #endregion
        #region Singleton pro cache ukládání předkompilovaných vzorců a pluginů konkrétních funkcí
        /// <summary>
        /// Konstruktor
        /// </summary>
        private Evaluator()
        {
            _FormulaDict = new Dictionary<string, CompiledFormula>();

            _NumberFormatter = new System.Globalization.NumberFormatInfo();
            _NumberFormatter.NumberDecimalDigits = 6;
            _NumberFormatter.NumberDecimalSeparator = ".";
            _NumberFormatter.NumberGroupSeparator = "";
            _NumberFormatter.NegativeSign = "-";

            _DateTimeFormatter = new System.Globalization.DateTimeFormatInfo();
            _DateTimeFormatter.DateSeparator = ".";
            _DateTimeFormatter.FullDateTimePattern = "dd.MM.yyyy HH:mm:ss";
        }
        /// <summary>
        /// Singleton Evaluatoru
        /// </summary>
        private static Evaluator Current
        {
            get
            {
                if (_Current == null)
                {
                    lock (_CurrentLock)
                    {
                        if (_Current == null)
                        {
                            _Current = new Evaluator();
                        }
                    }
                }
                return _Current;
            }
        }
        /// <summary>
        /// Zámek Singletonu
        /// </summary>
        private static object _CurrentLock = new object();
        /// <summary>
        /// Úložiště Singletonu
        /// </summary>
        private static Evaluator _Current;
        /// <summary>
        /// Instanční úložiště pro zkompilované vzorce (text = vzorec)
        /// </summary>
        private Dictionary<string, CompiledFormula> _FormulaDict;
        /// <summary>
        /// Instanční úložiště pro jednotlivé funkce, které mohou vyhodnocovat funkce v rámci vzorce. Obsahuje objekty popisující implementaci funkcí.
        /// </summary>
        private Dictionary<string, FunctionImplement> _FunctionDict = null;
        /// <summary>
        /// Úložiště pro Setting, který parsuje vstupní textové výrazy
        /// </summary>
        private MainParser.ParserSetting _Setting
        {
            get
            {
                if (this.__Setting == null)
                    this.__Setting = FormulaParserSetting.CreateParserSetting();
                return this.__Setting;
            }
        }
        private MainParser.ParserSetting __Setting = null;
        /// <summary>
        /// Úložiště pro NumberFormatInfo
        /// </summary>
        private System.Globalization.NumberFormatInfo _NumberFormatter;
        /// <summary>
        /// Úložiště pro DateTimeFormatInfo
        /// </summary>
        private System.Globalization.DateTimeFormatInfo _DateTimeFormatter;
        #endregion
        #region Kompiler
        /// <summary>
        /// Zkompiluje text výrazu a vrátí jej zkompilovaný.
        /// Pokud má výraz v cache, vrátí jej rovnou.
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        public static CompiledFormula Compile(string expression)
        {
            return Compile(expression, false);
        }
        /// <summary>
        /// Zkompiluje text výrazu a vrátí jej zkompilovaný.
        /// Je možno řídit přístup do cache.
        /// </summary>
        /// <param name="expression"></param>
        /// <param name="skipCache"></param>
        /// <returns></returns>
        public static CompiledFormula Compile(string expression, bool skipCache)
        {
            if (skipCache)
                return Current._CompileToFormula(expression);
            else
                return Current._GetFormula(expression);
        }
        /// <summary>
        /// Najde a vrátí zkompilovanou funkci pro daný text výrazu.
        /// Pokud daný výraz již byl kompilován, vrací odpovídající funkci z paměti.
        /// Pokud je výraz požadován poprvé, nejprve se zkompiluje na funkci a tu uloží do interní cache.
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        private CompiledFormula _GetFormula(string expression)
        {
            CompiledFormula formula;
            if (!this._FormulaDict.TryGetValue(expression, out formula))
            {
                formula = this._CompileToFormula(expression);
                this._FormulaDict.Add(expression, formula);
            }
            return formula;
        }
        /// <summary>
        /// Kompilátor funkce z výrazu
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        private CompiledFormula _CompileToFormula(string expression)
        {
            List<MainParser.ParserSegment> segments = MainParser.Parser.ParseString(expression, this._Setting);

            CompiledFormula formula = new CompiledFormula(expression, segments);

            int index = 0;
            foreach (MainParser.ParserSegment segment in segments)
                this._CompileToFormula(segment, ref index, formula);

            return formula;
        }
        /// <summary>
        /// Z celého dodaného segmentu, z jeho ValueList vytvoří a vrátí kompilovaný vzorec.
        /// </summary>
        /// <param name="segment"></param>
        /// <returns></returns>
        private CompiledFormula _CompileFormula(MainParser.ParserSegment segment)
        {
            CompiledFormula formula = new CompiledFormula();
            int index = 0;
            this._CompileToFormula(segment, ref index, formula);
            return formula;
        }
        /// <summary>
        /// Z dodaného segmentu z jeho hodnoty (index) a z následující závorky vytvoří a vrátí funkci.
        /// Funkce bude mít jméno a naplněny všechny parametry, které se v závorce najdou.
        /// Tato metoda posune parametr ref index tak, že po ukončení ukazuje na tu Value v segmentu, která obsahuje InnerSegment = zpracovanou závorku.
        /// Volající metoda si následně index inkrementuje a jde na další Value.
        /// </summary>
        /// <param name="segment"></param>
        /// <param name="index"></param>
        /// <returns></returns>
        private CompiledFunction _CompileFunction(MainParser.ParserSegment segment, ref int index)
        {
            MainParser.ParserSegmentValue valueFunction = segment.ValueList[index];
            MainParser.ParserSegmentValue valueParameters = _FindNextNonBlankValue(segment, ref index);

            CompiledFunction function = new CompiledFunction(valueFunction);

            // Kompilace parametrů z valueParameters.InnerSegmentu:
            if (valueParameters != null && valueParameters.ValueType == MainParser.ParserSegmentValueType.InnerSegment && valueParameters.InnerSegment.SegmentName == FormulaParserSetting.SEGMENT_PARENTHESIS)
            {
                MainParser.ParserSegment segmentParam = valueParameters.InnerSegment;
                int indexParam = 0;
                while (indexParam < segmentParam.ValueCount)
                {
                    CompiledFormula formula = new CompiledFormula();
                    bool isEnd = this._CompileToFormula(segmentParam, ref indexParam, formula);
                    function.AddParameter(formula);
                    if (isEnd) break;
                }
            }

            // Funkce je zkompilovaná:
            return function;
        }
        /// <summary>
        /// Obsah segmentu od daného indexu kompiluje (přidává) do předaného objektu CompiledFormula.
        /// Metoda skončí na konci segmentu nebo na čárce (=oddělovač parametrů).
        /// Vrací true = je konec / false = není konec, je jen čárka.
        /// </summary>
        /// <param name="segment"></param>
        /// <param name="index"></param>
        /// <param name="formula"></param>
        /// <returns></returns>
        private bool _CompileToFormula(MainParser.ParserSegment segment, ref int index, CompiledFormula formula)
        {
            while (index < segment.ValueCount)
            {
                MainParser.ParserSegmentValue value = segment.ValueList[index];
                CompiledExpressionType valueType = this._CompileDetectValueType(segment, index);
                switch (valueType)
                {
                    case CompiledExpressionType.Separator:
                        index++;
                        return false;
                    case CompiledExpressionType.Null:
                    case CompiledExpressionType.Bool:
                    case CompiledExpressionType.Int:
                    case CompiledExpressionType.Decimal:
                    case CompiledExpressionType.String:
                    case CompiledExpressionType.Variable:
                    case CompiledExpressionType.Operator:
                        formula.AddTextExpression(valueType, value);
                        break;
                    case CompiledExpressionType.InnerFormula:
                        // Tento druh hodnoty značí, že aktuální segment obsahuje Inner segment, který je závorkou. Zpracujeme vnitřní segment jako nový vzorec, a pak jej vložíme do zdejšího vzorce.
                        formula.AddInnerFormula(this._CompileFormula(value.InnerSegment));
                        break;

                    case CompiledExpressionType.Function:
                        formula.AddFunction(this._CompileFunction(segment, ref index));
                        break;
                }
                index++;
            }
            return true;
        }
        /// <summary>
        /// Detekuje, jaký druh prvku je právě nyní umístěn v daném segmentu na daném indexu.
        /// V případě potřeby se podívá kousek dopředu, aby dokázal odlišit proměnnou (=text, za kterým není závorka) od funkce (=text, za kterým je závorka).
        /// </summary>
        /// <param name="segment"></param>
        /// <param name="index"></param>
        /// <returns></returns>
        private CompiledExpressionType _CompileDetectValueType(MainParser.ParserSegment segment, int index)
        {
            if (index < 0 || index >= segment.ValueCount) return CompiledExpressionType.None;

            MainParser.ParserSegmentValue value = segment.ValueList[index];
            if (value == null || value.ValueType == MainParser.ParserSegmentValueType.Blank) return CompiledExpressionType.None;

            if (value.HasInnerSegment)
            {
                var innerSegment = value.InnerSegment;
                if (innerSegment.SegmentName == FormulaParserSetting.SEGMENT_STRING)
                {
                    return CompiledExpressionType.String;
                }
                else if (innerSegment.SegmentName == FormulaParserSetting.SEGMENT_PARENTHESIS)
                {
                    return CompiledExpressionType.InnerFormula;
                }
                return CompiledExpressionType.None;
            }
            else if (value.HasContent)
            {
                string content = value.Text;
                string contentL = content.ToLower();

                if (content == ",") return CompiledExpressionType.Separator;

                if (content == "+" ||
                    content == "-" ||
                    content == "*" ||
                    content == "/" ||
                    content == "%" ||
                    content == "<=" ||
                    content == ">=" ||
                    content == "<" ||
                    content == ">" ||
                    content == "=" ||
                    content == "==" ||
                    content == "<>" ||
                    content == "!=" ||
                    contentL == "and" ||
                    contentL == "or" ||
                    contentL == "xor" ||
                    contentL == "not" ||
                    contentL == "is")
                    return CompiledExpressionType.Operator;

                if (contentL == "null") return CompiledExpressionType.Null;
                if (contentL == "true" || contentL == "false") return CompiledExpressionType.Bool;
                if (content.Count(c => "0123456789".IndexOf(c) < 0) == 0) return CompiledExpressionType.Int;
                if (content.Count(c => "0123456789.".IndexOf(c) < 0) == 0) return CompiledExpressionType.Decimal;

                // Máme text, který není ani operátorem, ani holé číslo (125.25): může to být funkce nebo proměnná.
                // Funkce to je, když nejbližší další (NotBlank) Value je segment v závorce:
                MainParser.ParserSegmentValue valueNext = _FindNextNonBlankValue(segment, ref index);
                if (valueNext != null && valueNext.ValueType == MainParser.ParserSegmentValueType.InnerSegment && valueNext.InnerSegment.SegmentName == FormulaParserSetting.SEGMENT_PARENTHESIS)
                    return CompiledExpressionType.Function;

                // ... jinak jde o proměnnou (nebo o konstantu nebo co):
                return CompiledExpressionType.Variable;
            }
            return CompiledExpressionType.None;
        }
        /// <summary>
        /// Metoda vyhledá a vrátí nejbližší následující prvek Value z daného segmentu za daným indexem (tedy nikoli na daném indexu, ale +1), 
        /// přičemž hledá Value, který není typu Blank.
        /// Pokud do konce segmentu nic nenajde, vrací null.
        /// Předaný parametr ref index aktualizuje pouze v případě, kdy najde vyhovující prvek. Pokud vrací null, pak index nemění.
        /// </summary>
        /// <param name="segment"></param>
        /// <param name="index"></param>
        /// <returns></returns>
        private MainParser.ParserSegmentValue _FindNextNonBlankValue(MainParser.ParserSegment segment, ref int index)
        {
            for (int s = index + 1; s < segment.ValueCount; s++)
            {
                if (segment.ValueList[s].ValueType != MainParser.ParserSegmentValueType.Blank)
                {
                    index = s;
                    return segment.ValueList[s];
                }
            }
            return null;
        }
        #endregion
        #region Pluginy funkcí
        /// <summary>
        /// Najde a vrátí instanci třídy, která chce vyhodnocovat funkci daného jména
        /// </summary>
        /// <param name="functionName"></param>
        /// <returns></returns>
        private FunctionImplement _FindFunction(string functionName)
        {
            if (this._FunctionDict == null)
                this._FunctionDict = _CreateFunctionDict();

            string key = functionName;
            FunctionImplement implement;
            if (this._FunctionDict.TryGetValue(key, out implement))
                return implement;

            key = key.ToLower();
            if (this._FunctionDict.TryGetValue(key, out implement) && !implement.FunctionNameCaseSensitive)
                return implement;

            throw new EvalException("Ve výrazu je použita neznámá funkce: " + functionName);
        }
        /// <summary>
        /// Vytvoří dictionary s jmény funkcí a jejich výkonnými objekty.
        /// </summary>
        /// <returns></returns>
        private static Dictionary<string, FunctionImplement> _CreateFunctionDict()
        {
            Dictionary<string, FunctionImplement> result = new Dictionary<string, FunctionImplement>();
            IEnumerable<IExpressionFunction> providers = Assistant.GetServiceObjectList<IExpressionFunction>(true);
            foreach (IExpressionFunction provider in providers)
            {   // Projdu providery (pluginy IExpressionFunction):
                List<FunctionImplement> implements = FunctionImplement.SearchProvider(provider);
                foreach (FunctionImplement implement in implements)
                {   // Projdu jeho metody, které jsou implemetacemi funkcí:
                    FunctionImplement other;
                    if (result.TryGetValue(implement.Key, out other))
                        throw new EvalException("Funkce " + implement.ImplementInMethod + " se hlásí ke jménu " + implement.Key + ", k němu se ale již dříve přihlásila funkce " + other.ImplementInMethod);
                    result.Add(implement.Key, implement);
                }
            }
            return result;
        }
        #endregion
        #region Konstanty
        public const string LISTGROUP_GENERAL = "General";
        public const string LISTGROUP_LOGICAL = "Logical";
        public const string LISTGROUP_NUMERIC = "Numeric";
        public const string LISTGROUP_TEXT = "Text";
        public const string LISTGROUP_DATETIME = "DateTime";
        #endregion
    }
    #endregion
    #region class CompiledFormula : kompilovaný vzorec, včetně metod pro vyhodnocení. Obsahuje lineární řadu kompilovaných výrazů CompiledExpression.
    /// <summary>
    /// CompiledFormula : kompilovaný vzorec, včetně metod pro vyhodnocení. Obsahuje lineární řadu kompilovaných výrazů CompiledExpression.
    /// Jeden vzorec má charakter jedné lineární nepřerušované řady výrazů, které se pomocí operátorů vyhodnocují, typicky: "2 + 6 * 8 - 3 / 9".
    /// Jednotlivý výraz je konstanta (číslo, string, bool, null), nebo operátor (+ - * / and or is not), 
    /// nebo proměnná (pagenumber, currentdate), nebo funkce (max(...), acos(...), if (condition, ex1, ex2),
    /// anebo vnořené výrazy = zadané v závorce a vyhodnocované přednostně: "2 * (4 + 6 / (8 - 3))".
    /// V případě funkce je ve výrazu uložena funkce, která má svůj seznam parametrů, což jsou kupodivu zase vzorce.
    /// </summary>
    public class CompiledFormula
    {
        #region Konstrukce
        /// <summary>
        /// Konstruktor
        /// </summary>
        public CompiledFormula()
        {
            this.Expressions = new List<CompiledExpression>();
        }
        /// <summary>
        /// Konstruktor
        /// </summary>
        /// <param name="expression"></param>
        /// <param name="segments"></param>
        public CompiledFormula(string expression, List<MainParser.ParserSegment> segments)
        {
            this.Expressions = new List<CompiledExpression>();
            this.Expression = expression;
            this.Segments = segments;
        }
        /// <summary>
        /// Vstupní textový vzorec, který je v tomto kompilovaném vzorci obsažen.
        /// Tato property je naplněna pouze v TopLevel vzorci; vzorce nižších úrovní zde mají null.
        /// </summary>
        public string Expression { get; private set; }
        /// <summary>
        /// Sada parsovaných segmentů, které jsou v tomto kompilovaném vzorci obsaženy.
        /// Tato property je naplněna pouze v TopLevel vzorci; vzorce nižších úrovní zde mají null.
        /// </summary>
        public List<MainParser.ParserSegment> Segments { get; set; }
        /// <summary>
        /// Počet výrazů v poli this.Expressions
        /// </summary>
        public int ExpressionsCount { get { return this.Expressions.Count; } }
        /// <summary>
        /// Sada výrazů
        /// </summary>
        public List<CompiledExpression> Expressions { get; private set; }
        /// <summary>
        /// Vizualizace
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return this.Text;
        }
        /// <summary>
        /// Obsahuje aktuální textovou podobu vzorce, odpovídá vstupnímu textu před parsováním.
        /// </summary>
        public string Text
        {
            get
            {
                StringBuilder sb = new StringBuilder();
                foreach (CompiledExpression expression in this.Expressions)
                    sb.Append((sb.Length == 0 ? "" : " ") + expression.Text);
                return sb.ToString();
            }
        }
        #endregion
        #region Přidávání výrazů
        /// <summary>
        /// Přidá textový výraz
        /// </summary>
        /// <param name="type"></param>
        /// <param name="text"></param>
        public void AddTextExpression(CompiledExpressionType type, MainParser.ParserSegmentValue value)
        {
            this.Expressions.Add(new CompiledExpression(type, value));
        }
        /// <summary>
        /// Přidá vnořený (závorkovaný) vzorec
        /// </summary>
        /// <param name="compiledFormula"></param>
        public void AddInnerFormula(CompiledFormula compiledFormula)
        {
            this.Expressions.Add(new CompiledExpression(compiledFormula));
        }
        /// <summary>
        /// Přidá funkci
        /// </summary>
        /// <param name="function"></param>
        public void AddFunction(CompiledFunction function)
        {
            this.Expressions.Add(new CompiledExpression(function));
        }
        #endregion
        #region Vyhodnocení vzorce
        /// <summary>
        /// Metoda vrátí hodnotu tohoto objektu, v daném kontextu.
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        internal EValue Evaluate(IEvaluateContext context)
        {
            // Zde vyhodnocuji řadu výrazů za pomoci operátorů mezi nimi uvedených, jako příklad: "25 + 4 * 3 - 16 * 8 ^ 2".
            // Veškeré závorky a funkce jsou řešeny jinde (totiž: uvnitř metody CompiledExpression.Evaluate(context)), zde od nich abstrahuji.
            // Úkolem zdejší metody je řešit operace mezi hodnotami a řešit prioritu operátorů.
            int index = 0;
            EValue result = this.EvaluateLevel(1, ref index, context);
            return result;
        }

        private EValue EvaluateLevel(int levelPriority, ref int index, IEvaluateContext context)
        {
            int count = this.Expressions.Count;
            int currentPriority = 0;            // Aktuální nejnižší úroveň priority, průběžně se nastavuje nejnižší dosažená, a pokud narazíme na operátor vyšší priority pak následující blok řešíme jako izolovanou grupu
            EValue value = this.EvaluateExpression(ref index, context);

            while (index < count)
            {
                // Pokud aktuální operátor má prioritu nižší, než je priorita zdejšího levelu, pak skončíme a vrátíme dosavadní value:
                CompiledExpression operator1 = this.Expressions[index];        // Aktuální výraz, měl by to být operátor
                int priority1 = operator1.OperatorPriority ;
                if (priority1 < levelPriority)
                    break;
                index++;

                // V proměnné currentPriority držíme nejnižší reálnou úroveň priority v aktuálním levelu:
                if (currentPriority == 0 || priority1 < currentPriority)
                    currentPriority = priority1;

                // Najdeme příští výraz, a najdeme operátor za ním (pokud by měl vyšší prioritu, pak se musí zpracovat přednostně):
                CompiledExpression operator2 = null;                           // Vyhledám další operátor (za příští hodnotou)
                int i = index;
                while (i < count)
                {
                    CompiledExpression exp = this.Expressions[i];
                    if (exp.ExpressionType != CompiledExpressionType.Operator)
                    {   // Našli jsme výraz, který není operátor (pozor, přeskočili jsme i unární operátor) = je to reálný výraz k vyhodnocení.
                        // Podíváme se hned za něj, a pokud tam bude operátor, načtu si jej - to vše jen kvůli jeho prioritě:
                        i++;
                        if (i < count && this.Expressions[i].ExpressionType == CompiledExpressionType.Operator)
                            operator2 = this.Expressions[i];
                        break;
                    }
                    // Hledáme na další pozici, jestli tam bude výraz (ne operátor):
                    i++;
                }

                // Vyhodnocení dalšího výrazu
                EValue nextValue = EValue.Empty;
                if (operator2 != null && operator2.OperatorPriority > currentPriority)
                    nextValue = this.EvaluateLevel((currentPriority + 1), ref index, context);
                else
                    nextValue = this.EvaluateExpression(ref index, context);

                // Provedeme vyhodnocení dvou hodnot s pomocí operátoru (value = value oper1 nextValue)
                value = this.EvaluateOperator(value, operator1, nextValue);
            }
            return value;
        }
        /// <summary>
        /// Vyhodnotí výraz, na který ukazuje pointer.
        /// Pokud pointer ukazuje na unární operátor, načte jej, a načte následující výraz a vyhodnotí je společně a vrátí.
        /// Při výstupu ukazuje index na následující položku (typicky příští operátor, nebo na konec).
        /// </summary>
        /// <param name="index"></param>
        /// <param name="context"></param>
        /// <returns></returns>
        private EValue EvaluateExpression(ref int index, IEvaluateContext context)
        {
            if (index < 0 || index >= this.Expressions.Count) return EValue.Empty;
            
            CompiledExpression unaryOperator = null;
            CompiledExpression expression = this.Expressions[index];
            if (IsUnaryOperator(expression))
            {
                unaryOperator = expression;
                index++;
                if (index >= this.Expressions.Count) return EValue.Empty;
                expression = this.Expressions[index];
            }
            index++;

            return this.EvaluateExpression(unaryOperator, expression, context);
        }
        /// <summary>
        /// Vyhodnotí výraz včetně jeho unárního operátoru.
        /// </summary>
        /// <param name="unaryOperator"></param>
        /// <param name="expression"></param>
        /// <param name="context"></param>
        /// <returns></returns>
        private EValue EvaluateExpression(CompiledExpression unaryOperator, CompiledExpression expression, IEvaluateContext context)
        {
            EValue value = expression.Evaluate(context);
            if (unaryOperator != null)
            {
                switch (unaryOperator.TextValue)
                {
                    case "-":
                        value = new EValue() { ValueDecimal = 0m } - value;
                        break;
                    case "!":
                        value = new EValue() { ValueBoolean = !value.ValueBoolean };
                        break;
                }
            }
            return value;
        }
        /// <summary>
        /// Vyhodnotí atomární operaci: X = A op B
        /// </summary>
        /// <param name="value1">Operand 1</param>
        /// <param name="operation">Operátor</param>
        /// <param name="value2">Operand 2</param>
        /// <returns>Výsledek</returns>
        private EValue EvaluateOperator(EValue value1, CompiledExpression operation, EValue value2)
        {
            string operat = operation.TextValue.ToLower();
            switch (operat)
            {
                case "or": return value1.ValueBoolean || value2.ValueBoolean;
                case "||": return value1.ValueBoolean || value2.ValueBoolean;
                case "and": return value1.ValueBoolean && value2.ValueBoolean;
                case "&&": return value1.ValueBoolean && value2.ValueBoolean;

                case "<": return value1 < value2;
                case "<=": return value1 <= value2;
                case ">": return value1 > value2;
                case ">=": return value1 >= value2;

                case "<>": return value1 != value2;
                case "!=": return value1 != value2;
                case "==": return value1 == value2;
                case "=": return value1 == value2;

                case "+": return value1 + value2;
                case "-": return value1 - value2;
                case "*": return value1 * value2;
                case "/": return value1 / value2;
            }
            throw new EvalException("Neznámý operátor: " + operation.TextValue);
        }
        /// <summary>
        /// Vrací true, pokud daný výraz je unární operátor
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        private bool IsUnaryOperator(CompiledExpression expression)
        {
            if (expression.ExpressionType != CompiledExpressionType.Operator) return false;
            string text = expression.TextValue;
            return (text == "-" || text == "!");
        }
        #endregion
    }
    #endregion
    #region class CompiledExpression : jedna část kompilovaného vzorce.
    /// <summary>
    /// CompiledExpression : jedna část kompilovaného vzorce.
    /// Obsahuje operátor, konstantu, funkci, nebo další vnořený kompilovaný vzorec.
    /// </summary>
    public class CompiledExpression
    {
        #region Konstrukce
        internal CompiledExpression(CompiledExpressionType expressionType, MainParser.ParserSegmentValue value)
        {
            this.ExpressionType = expressionType;
            this.TextValue = value.InnerText;
            this.InnerFormula = null;
            this.Function = null;
            this.ValueFirst = value;
        }
        internal CompiledExpression(CompiledFormula innerFormula)
        {
            this.ExpressionType = CompiledExpressionType.InnerFormula;
            this.TextValue = null;
            this.InnerFormula = innerFormula;
            this.Function = null;
            this.ValueFirst = (innerFormula.ExpressionsCount > 0 ? innerFormula.Expressions[0].ValueFirst : (MainParser.ParserSegmentValue)null);
        }
        internal CompiledExpression(CompiledFunction function)
        {
            this.ExpressionType = CompiledExpressionType.Function;
            this.TextValue = null;
            this.InnerFormula = null;
            this.Function = function;
            this.ValueFirst = function.ValueFirst;
        }
        /// <summary>
        /// Vizualizace
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return this.Text;
        }
        /// <summary>
        /// Obsahuje aktuální textovou podobu vzorce, odpovídá vstupnímu textu před parsováním.
        /// </summary>
        public string Text
        {
            get
            {
                if (this.TextValue != null)
                {
                    if (this.ExpressionType == CompiledExpressionType.String)
                        return "\"" + this.TextValue + "\"";
                    else
                        return this.TextValue;
                }
                if (this.InnerFormula != null)
                    return "(" + this.InnerFormula.Text + ")";
                if (this.Function != null)
                    return this.Function.Text;
                return "{" + this.ExpressionType.ToString() + "}";

                //string result = this.ExpressionType.ToString() + ": ";
                //if (this.TextValue != null)
                //{
                //    if (this.ExpressionType == CompiledExpressionType.String)
                //        return result + "\"" + this.TextValue + "\"";
                //    else
                //        return result + this.TextValue;
                //}
                //if (this.InnerFormula != null)
                //    return result + "(" + this.InnerFormula.Text + ")";
                //if (this.Function != null)
                //    return result + this.Function.Text;
                //return result + "???";

            }
        }
        /// <summary>
        /// Druh výrazu
        /// </summary>
        internal CompiledExpressionType ExpressionType { get; private set; }
        /// <summary>
        /// Čistý text pro výrazy, které nejsou InnerFormula ani Function. U stringu bez uvozovek. U čísel samozřejmě bez konverze na číselný typ.
        /// </summary>
        internal string TextValue { get; private set; }
        /// <summary>
        /// Vzorec vložený v závorkách jako jeden výraz
        /// </summary>
        internal CompiledFormula InnerFormula { get; private set; }
        /// <summary>
        /// Funkce s parametry (uvnitř)
        /// </summary>
        internal CompiledFunction Function { get; private set; }
        /// <summary>
        /// První objekt typu ParserSegmentValue, který je v tomto výrazu obsažen
        /// </summary>
        public MainParser.ParserSegmentValue ValueFirst { get; private set; }
        /// <summary>
        /// Priorita operátoru: 0=nejde o operátor, 1=nejnižší, 2=vyšší (zpracovává se přednostně před 1), atd.
        /// Řeší i logické operátory a operátory porovnání
        /// </summary>
        internal int OperatorPriority
        {
            get
            {
                if (this.ExpressionType != CompiledExpressionType.Operator) return 0;
                string operation = this.TextValue.ToLower();
                if (operation == "or" || operation == "||") return 1;
                if (operation == "and" || operation == "&&") return 2;
                if (operation == "<" || operation == ">" || operation == "<=" || operation == ">=" || 
                    operation == "==" || operation == "=" || operation == "!=" || operation == "<>") return 11;
                if (operation == "&" || operation == "|") return 21;
                if (operation == "+" || operation == "-") return 31;
                if (operation == "*" || operation == "/") return 41;
                return 51;
            }
        }
        #endregion
        #region Vyhodnocení výrazu
        /// <summary>
        /// Metoda vrátí hodnotu tohoto objektu, v daném kontextu.
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        internal EValue Evaluate(IEvaluateContext context)
        {
            if (this.TextValue != null)
                return this.EvaluateText(context);
            if (this.InnerFormula != null)
                return this.InnerFormula.Evaluate(context);
            if (this.Function != null)
                return this.Function.Evaluate(context);
            return EValue.Empty;
        }
        /// <summary>
        /// Vrátí hodnotu tohoto výrazu, pokud je to TextValue.
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        private EValue EvaluateText(IEvaluateContext context)
        {
            switch (this.ExpressionType)
            {
                case CompiledExpressionType.Variable:
                    return context.GetValueOfVariable(this.TextValue);
                case CompiledExpressionType.Bool:
                    return new EValue() { ValueBoolean = (this.TextValue.ToLower() == "true") };
                case CompiledExpressionType.Int:
                    Int64 valueInt;
                    if (Int64.TryParse(this.TextValue, System.Globalization.NumberStyles.Any, Evaluator.NumberFormatter, out valueInt))
                    {
                        if (valueInt <= Int32.MaxValue && valueInt >= Int32.MinValue)
                            return new EValue() { ValueInt32 = (Int32)valueInt };
                        return new EValue() { ValueInt64 = valueInt };
                    }
                    return new EValue() { ValueInt32 = 0 };
                case CompiledExpressionType.Decimal:
                    Decimal valueDec;
                    if (Decimal.TryParse(this.TextValue, System.Globalization.NumberStyles.Any, Evaluator.NumberFormatter, out valueDec))
                    {
                        return new EValue() { ValueDecimal = valueDec };
                    }
                    return new EValue() { ValueDecimal = 0m };
                case CompiledExpressionType.String:
                    return new EValue() { ValueString = this.TextValue };
            }
            return EValue.Empty;
        }
        #endregion
    }
    #endregion
    #region class CompiledFunction : kompilovaná funkce. Obsahuje název, obsahuje referenci na objekt, který tuto funkci reprezentuje, obsahuje kompilované parametry.
    /// <summary>
    /// CompiledFunction : kompilovaná funkce. Obsahuje název, obsahuje referenci na objekt, který tuto funkci reprezentuje, obsahuje kompilované parametry (které lze snadno vyhodnotit na konkrétní hodnoty).
    /// </summary>
    public class CompiledFunction
    {
        #region Konstrukce, přidávání parametrů
        /// <summary>
        /// Konstruktor s uvedením jména funkce.
        /// Objekt do sebe doplní výkonný objekt (plugin funkce) za pomoci metody Evaluator.FindFunction(functionName).
        /// </summary>
        /// <param name="functionName"></param>
        internal CompiledFunction(MainParser.ParserSegmentValue valueFunction)
        {
            this.ValueFirst = valueFunction;
            this.FunctionName = valueFunction.InnerText;
            this.Function = Evaluator.FindFunction(valueFunction.InnerText);
            this.ParameterList = new List<CompiledFormula>();
        }
        /// <summary>
        /// Vizualizace
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return this.Text;
        }
        /// <summary>
        /// Obsahuje aktuální textovou podobu vzorce, odpovídá vstupnímu textu před parsováním.
        /// </summary>
        public string Text
        {
            get
            {
                StringBuilder sb = new StringBuilder();
                foreach (CompiledFormula parameter in this.ParameterList)
                    sb.Append((sb.Length == 0 ? "" : ", ") + parameter.Text);
                return this.FunctionName + "(" + sb.ToString() + ")";
            }
        }
        /// <summary>
        /// Objekt ParserSegmentValue, který první patří do této funkce = obsahuje její název.
        /// </summary>
        public MainParser.ParserSegmentValue ValueFirst { get; private set; }
        /// <summary>
        /// Jméno funkce tak jak bylo vepsáno ve vzorci. Nemá mezery na začátku ani na konci. Velikost písmen není změněna.
        /// </summary>
        internal string FunctionName { get; private set; }
        /// <summary>
        /// Instance výkonného objektu funkce, která provede vyhodnocení hodnoty funkce na základě parametrů
        /// </summary>
        internal FunctionImplement Function { get; private set; }
        /// <summary>
        /// Soupis parametrů. Parametry jsou kompilované vzorce separované ze závorky funkce.
        /// </summary>
        internal List<CompiledFormula> ParameterList { get; private set; }
        /// <summary>
        /// Přidá do parametrů funkce další vzorec
        /// </summary>
        /// <param name="formula"></param>
        internal void AddParameter(CompiledFormula formula)
        {
            this.ParameterList.Add(formula);
        }
        #endregion
        #region Vyhodnocení vzorce
        /// <summary>
        /// Metoda vrátí hodnotu tohoto objektu, v daném kontextu.
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        internal EValue Evaluate(IEvaluateContext context)
        {
            if (this.Function == null) return EValue.Empty;
            return this.Function.Evaluate(this.ParameterList, context);
        }
        #endregion
    }
    /// <summary>
    /// Druh výrazu
    /// </summary>
    public enum CompiledExpressionType
    {
        None = 0,
        /// <summary>
        /// Oddělovač (například parametrů) = čárka
        /// </summary>
        Separator,
        /// <summary>
        /// Hodnota Null, neboli Empty
        /// </summary>
        Null,
        /// <summary>
        /// Boolean konstanta (true nebo false)
        /// </summary>
        Bool,
        /// <summary>
        /// Číslice bez tečky
        /// </summary>
        Int,
        /// <summary>
        /// Číslice s tečkou
        /// </summary>
        Decimal,
        /// <summary>
        /// Řetězec (by v uvozovkách, v property TextValue není)
        /// </summary>
        String,
        /// <summary>
        /// Název proměnné (text bez uvozovek, za nímž není závorka ale třeba operátor nebo jiný text).
        /// </summary>
        Variable,
        /// <summary>
        /// Část vzorce v závorce, jako v příkladu: "12 * (4 + 2)"
        /// </summary>
        InnerFormula,
        /// <summary>
        /// Název funkce, například slovo "abs" v příkladu: "25 + abs(-60)"
        /// </summary>
        Function,
        /// <summary>
        /// Operátor, který se vyhodnocuje v rámci jedné instance CompiledFormula.
        /// Operátor je typicky + - * / a další logické operátory "and" "or" ...
        /// </summary>
        Operator
    }
    #endregion
    #region class EValue : nositel a konvertor hodnoty základních datových typů
    /// <summary>
    /// Struktura, která v sobě udrží hodnotu typu String, Int, Decimal, DateTime a dokáže ji implicitně konvertovat.
    /// Navíc obsahuje příznak, jakého typu hodnota je a na co ji lze konvertovat.
    /// </summary>
    public struct EValue
    {
        #region Konstrukce
        public override string ToString()
        {
            switch (this._Type)
            {
                case EValueDataType.None: return "{None}";
                case EValueDataType.Int32: return this._ValueInt32.ToString();
                case EValueDataType.Int64: return this._ValueInt64.ToString();
                case EValueDataType.Decimal: return this._ValueDecimal.ToString();
                case EValueDataType.DateTime: return this._ValueDateTime.ToString("F", Evaluator.DateTimeFormatter);
                case EValueDataType.String: return (this._ValueString == null ? "{null}" : this._ValueString);
            }
            return "???";
        }
        #endregion
        #region Implicit convertors
        public static implicit operator Boolean(EValue eValue) { return eValue.ValueBoolean; }
        public static implicit operator EValue(Boolean value) { return new EValue() { ValueBoolean = value }; }
        public static implicit operator Int32(EValue eValue) { return eValue.ValueInt32; }
        public static implicit operator EValue(Int32 value) { return new EValue() { ValueInt32 = value }; }
        public static implicit operator Int64(EValue eValue) { return eValue.ValueInt64; }
        public static implicit operator EValue(Int64 value) { return new EValue() { ValueInt64 = value }; }
        public static implicit operator Decimal(EValue eValue) { return eValue.ValueDecimal; }
        public static implicit operator EValue(Decimal value) { return new EValue() { ValueDecimal = value }; }
        public static implicit operator EValue(Single value) { return new EValue() { ValueDecimal = (Decimal)value }; }
        public static implicit operator EValue(Double value) { return new EValue() { ValueDecimal = (Decimal)value }; }
        public static implicit operator String(EValue eValue) { return eValue.ValueString; }
        public static implicit operator EValue(String value) { return new EValue() { ValueString = value }; }
        #endregion
        #region Operátory
        public static EValue operator +(EValue a, EValue b)
        {
            EValueDataType commonType = _GetCommonType(a, b, _Operation.Add);
            switch (commonType)
            {
                case EValueDataType.Boolean:
                    return new EValue() { ValueBoolean = (a.ValueBoolean || b.ValueBoolean) };
                case EValueDataType.Int32:
                    return new EValue() { ValueInt32 = a.ValueInt32 + b.ValueInt32 };
                case EValueDataType.Int64:
                    return new EValue() { ValueInt64 = a.ValueInt64 + b.ValueInt64 };
                case EValueDataType.Decimal:
                    return new EValue() { ValueDecimal = a.ValueDecimal + b.ValueDecimal };
                case EValueDataType.DateTime:
                    return new EValue() { ValueDateTime = a.ValueDateTime.AddDays((double)b.ValueDecimal) };
                case EValueDataType.String:
                    return new EValue() { ValueString = a.ValueString + b.ValueString };
            }
            throw new EvalException("Nelze sloučit dvě hodnoty, kde první je " + a._Type.ToString() + " a druhá " + b._Type.ToString());
        }
        public static EValue operator -(EValue a, EValue b)
        {
            EValueDataType commonType = _GetCommonType(a, b, _Operation.Sub);
            switch (commonType)
            {
                case EValueDataType.Boolean:
                    return new EValue() { ValueBoolean = (a.ValueBoolean ^ b.ValueBoolean) };
                case EValueDataType.Int32:
                    return new EValue() { ValueInt32 = a.ValueInt32 - b.ValueInt32 };
                case EValueDataType.Int64:
                    return new EValue() { ValueInt64 = a.ValueInt64 - b.ValueInt64 };
                case EValueDataType.Decimal:
                    return new EValue() { ValueDecimal = a.ValueDecimal - b.ValueDecimal };
                case EValueDataType.DateTime:
                    return new EValue() { ValueDateTime = a.ValueDateTime.AddDays((double)-b.ValueDecimal) };
            }
            throw new EvalException("Nelze odečíst dvě hodnoty, kde první je " + a._Type.ToString() + " a druhá " + b._Type.ToString());
        }
        public static EValue operator *(EValue a, EValue b)
        {
            EValueDataType commonType = _GetCommonType(a, b, _Operation.Multiply);
            switch (commonType)
            {
                case EValueDataType.Boolean:
                    return new EValue() { ValueBoolean = (a.ValueBoolean && b.ValueBoolean) };
                case EValueDataType.Int32:
                    return new EValue() { ValueInt32 = a.ValueInt32 * b.ValueInt32 };
                case EValueDataType.Int64:
                    return new EValue() { ValueInt64 = a.ValueInt64 * b.ValueInt64 };
                case EValueDataType.Decimal:
                    return new EValue() { ValueDecimal = a.ValueDecimal * b.ValueDecimal };
            }
            throw new EvalException("Nelze násobit dvě hodnoty, kde první je " + a._Type.ToString() + " a druhá " + b._Type.ToString());
        }
        public static EValue operator /(EValue a, EValue b)
        {
            EValueDataType commonType = _GetCommonType(a, b, _Operation.Multiply);
            switch (commonType)
            {
                case EValueDataType.Int32:
                    return new EValue() { ValueInt32 = a.ValueInt32 / b.ValueInt32 };
                case EValueDataType.Int64:
                    return new EValue() { ValueInt64 = a.ValueInt64 / b.ValueInt64 };
                case EValueDataType.Decimal:
                    return new EValue() { ValueDecimal = a.ValueDecimal / b.ValueDecimal };
            }
            throw new EvalException("Nelze dělit dvě hodnoty, kde první je " + a._Type.ToString() + " a druhá " + b._Type.ToString());
        }

        public static EValue operator ==(EValue a, EValue b)
        {
            EValueDataType commonType = _GetCommonType(a, b, _Operation.Compare);
            switch (commonType)
            {
                case EValueDataType.Boolean:
                    return new EValue() { ValueBoolean = (a.ValueBoolean == b.ValueBoolean) };
                case EValueDataType.Int32:
                    return new EValue() { ValueBoolean = a.ValueInt32 == b.ValueInt32 };
                case EValueDataType.Int64:
                    return new EValue() { ValueBoolean = a.ValueInt64 == b.ValueInt64 };
                case EValueDataType.Decimal:
                    return new EValue() { ValueBoolean = a.ValueDecimal == b.ValueDecimal };
                case EValueDataType.DateTime:
                    return new EValue() { ValueBoolean = a.ValueDateTime == b.ValueDateTime };
                case EValueDataType.String:
                    return new EValue() { ValueBoolean = a.ValueString == b.ValueString };
            }
            throw new EvalException("Nelze srovnávat dvě hodnoty, kde první je " + a._Type.ToString() + " a druhá " + b._Type.ToString());
        }
        public static EValue operator !=(EValue a, EValue b)
        {
            EValueDataType commonType = _GetCommonType(a, b, _Operation.Compare);
            switch (commonType)
            {
                case EValueDataType.Boolean:
                    return new EValue() { ValueBoolean = (a.ValueBoolean != b.ValueBoolean) };
                case EValueDataType.Int32:
                    return new EValue() { ValueBoolean = a.ValueInt32 != b.ValueInt32 };
                case EValueDataType.Int64:
                    return new EValue() { ValueBoolean = a.ValueInt64 != b.ValueInt64 };
                case EValueDataType.Decimal:
                    return new EValue() { ValueBoolean = a.ValueDecimal != b.ValueDecimal };
                case EValueDataType.DateTime:
                    return new EValue() { ValueBoolean = a.ValueDateTime != b.ValueDateTime };
                case EValueDataType.String:
                    return new EValue() { ValueBoolean = a.ValueString != b.ValueString };
            }
            throw new EvalException("Nelze srovnávat dvě hodnoty, kde první je " + a._Type.ToString() + " a druhá " + b._Type.ToString());
        }

        public static EValue operator >(EValue a, EValue b)
        {
            EValueDataType commonType = _GetCommonType(a, b, _Operation.Compare);
            switch (commonType)
            {
                case EValueDataType.Int32:
                    return new EValue() { ValueBoolean = a.ValueInt32 > b.ValueInt32 };
                case EValueDataType.Int64:
                    return new EValue() { ValueBoolean = a.ValueInt64 > b.ValueInt64 };
                case EValueDataType.Decimal:
                    return new EValue() { ValueBoolean = a.ValueDecimal > b.ValueDecimal };
                case EValueDataType.DateTime:
                    return new EValue() { ValueBoolean = a.ValueDateTime > b.ValueDateTime };
                case EValueDataType.String:
                    return new EValue() { ValueBoolean = a.ValueString.CompareTo(b.ValueString) > 0 };
            }
            throw new EvalException("Nelze určit vztah dvou hodnot, kde první je " + a._Type.ToString() + " a druhá " + b._Type.ToString());
        }
        public static EValue operator <(EValue a, EValue b)
        {
            EValueDataType commonType = _GetCommonType(a, b, _Operation.Compare);
            switch (commonType)
            {
                case EValueDataType.Int32:
                    return new EValue() { ValueBoolean = a.ValueInt32 < b.ValueInt32 };
                case EValueDataType.Int64:
                    return new EValue() { ValueBoolean = a.ValueInt64 < b.ValueInt64 };
                case EValueDataType.Decimal:
                    return new EValue() { ValueBoolean = a.ValueDecimal < b.ValueDecimal };
                case EValueDataType.DateTime:
                    return new EValue() { ValueBoolean = a.ValueDateTime < b.ValueDateTime };
                case EValueDataType.String:
                    return new EValue() { ValueBoolean = a.ValueString.CompareTo(b.ValueString) < 0 };
            }
            throw new EvalException("Nelze určit vztah dvou hodnot, kde první je " + a._Type.ToString() + " a druhá " + b._Type.ToString());
        }
        public static EValue operator >=(EValue a, EValue b)
        {
            EValueDataType commonType = _GetCommonType(a, b, _Operation.Compare);
            switch (commonType)
            {
                case EValueDataType.Int32:
                    return new EValue() { ValueBoolean = a.ValueInt32 >= b.ValueInt32 };
                case EValueDataType.Int64:
                    return new EValue() { ValueBoolean = a.ValueInt64 >= b.ValueInt64 };
                case EValueDataType.Decimal:
                    return new EValue() { ValueBoolean = a.ValueDecimal >= b.ValueDecimal };
                case EValueDataType.DateTime:
                    return new EValue() { ValueBoolean = a.ValueDateTime >= b.ValueDateTime };
                case EValueDataType.String:
                    return new EValue() { ValueBoolean = a.ValueString.CompareTo(b.ValueString) >= 0 };
            }
            throw new EvalException("Nelze určit vztah dvou hodnot, kde první je " + a._Type.ToString() + " a druhá " + b._Type.ToString());
        }
        public static EValue operator <=(EValue a, EValue b)
        {
            EValueDataType commonType = _GetCommonType(a, b, _Operation.Compare);
            switch (commonType)
            {
                case EValueDataType.Int32:
                    return new EValue() { ValueBoolean = a.ValueInt32 <= b.ValueInt32 };
                case EValueDataType.Int64:
                    return new EValue() { ValueBoolean = a.ValueInt64 <= b.ValueInt64 };
                case EValueDataType.Decimal:
                    return new EValue() { ValueBoolean = a.ValueDecimal <= b.ValueDecimal };
                case EValueDataType.DateTime:
                    return new EValue() { ValueBoolean = a.ValueDateTime <= b.ValueDateTime };
                case EValueDataType.String:
                    return new EValue() { ValueBoolean = a.ValueString.CompareTo(b.ValueString) <= 0 };
            }
            throw new EvalException("Nelze určit vztah dvou hodnot, kde první je " + a._Type.ToString() + " a druhá " + b._Type.ToString());
        }

        /// <summary>
        /// Vrací typ, do něhož lze sloučit hodnotu daných dvou typů (typicky při sčítání)
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        private static EValueDataType _GetCommonType(EValue a, EValue b, _Operation operation)
        {
            return _GetCommonType(a._Type, b._Type, operation);
        }
        /// <summary>
        /// Vrací typ, do něhož lze sloučit hodnotu daných dvou typů (typicky při sčítání)
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        private static EValueDataType _GetCommonType(EValueDataType a, EValueDataType b, _Operation operation)
        {
            // Nezávislé na typu operace:
            if (a == EValueDataType.Decimal && (b == EValueDataType.Int32 || b == EValueDataType.Int64 || b == EValueDataType.Decimal)) return EValueDataType.Decimal;
            if (b == EValueDataType.Decimal && (a == EValueDataType.Int32 || a == EValueDataType.Int64 || a == EValueDataType.Decimal)) return EValueDataType.Decimal;
            if (a == EValueDataType.Int64 && (b == EValueDataType.Int32 || b == EValueDataType.Int64)) return EValueDataType.Int64;
            if (b == EValueDataType.Int64 && (a == EValueDataType.Int32 || a == EValueDataType.Int64)) return EValueDataType.Int64;
            if (a == EValueDataType.Int32 && b == EValueDataType.Int32) return EValueDataType.Int32;

            // Částečně závislé na typu operace:
            if (a == EValueDataType.String || b == EValueDataType.String)
            {
                if (operation == _Operation.Add || operation == _Operation.Compare)
                    return EValueDataType.String;
                return EValueDataType.None;
            }

            if (a == EValueDataType.Boolean || b == EValueDataType.Boolean)
            {
                if (operation == _Operation.Add || operation == _Operation.Sub || operation == _Operation.Multiply || operation == _Operation.Compare)
                    return EValueDataType.Boolean;
                return EValueDataType.None;
            }

            // Závislé na typu operace:
            if (a == EValueDataType.DateTime)
            {
                switch (operation)
                {
                    case _Operation.Add:
                    case _Operation.Sub:
                        if (b == EValueDataType.Int32 || b == EValueDataType.Int64 || b == EValueDataType.Decimal)
                            return EValueDataType.DateTime;
                        return EValueDataType.None;
                    case _Operation.Compare:
                        if (b == EValueDataType.DateTime)
                            return EValueDataType.DateTime;
                        return EValueDataType.None;
                }
                return EValueDataType.None;
            }

            return EValueDataType.None;
        }
        private enum _Operation { None, Add, Sub, Multiply, Compare }
        #endregion
        #region Public typové property
        public static EValue Empty { get { return new EValue(); } }
        public bool IsEmpty { get { return (this._Type == EValueDataType.None); } }
        public Boolean ValueBoolean
        {
            get
            {
                switch (this._Type)
                {
                    case EValueDataType.Boolean: return this._ValueBoolean;
                    case EValueDataType.Int32: return (this._ValueInt32 != 0);
                    case EValueDataType.Int64: return (this._ValueInt64 != 0);
                    case EValueDataType.Decimal: return (this._ValueDecimal != 0m);
                }
                throw new EvalException("Hodnotu typu " + this._Type.ToString() + " nelze konvertovat na Boolean.");
            }
            set
            {
                this._ValueBoolean = value;
                this._Type = EValueDataType.Boolean;
            }
        }
        public Int32 ValueInt32
        {
            get
            {
                switch (this._Type)
                {
                    case EValueDataType.Boolean: return (this._ValueBoolean ? (Int32)1 : (Int32)0);
                    case EValueDataType.Int32: return this._ValueInt32;
                    case EValueDataType.Int64: return (Int32)this._ValueInt64;
                    case EValueDataType.Decimal: return (Int32)this._ValueDecimal;
                    case EValueDataType.String: return Int32.Parse(this._ValueString);
                }
                throw new EvalException("Hodnotu typu " + this._Type.ToString() + " nelze konvertovat na Int32.");
            }
            set
            {
                this._ValueInt32 = value;
                this._Type = EValueDataType.Int32;
            }
        }
        public Int64 ValueInt64
        {
            get
            {
                switch (this._Type)
                {
                    case EValueDataType.Boolean: return (this._ValueBoolean ? (Int64)1 : (Int64)0);
                    case EValueDataType.Int32: return (Int64)this._ValueInt32;
                    case EValueDataType.Int64: return this._ValueInt64;
                    case EValueDataType.Decimal: return (Int64)this._ValueDecimal;
                    case EValueDataType.String: return Int64.Parse(this._ValueString);
                }
                throw new EvalException("Hodnotu typu " + this._Type.ToString() + " nelze konvertovat na Int64.");
            }
            set
            {
                this._ValueInt64 = value;
                this._Type = EValueDataType.Int64;
            }
        }
        public Decimal ValueDecimal
        {
            get
            {
                switch (this._Type)
                {
                    case EValueDataType.Boolean: return (this._ValueBoolean ? 1m : 0m);
                    case EValueDataType.Int32: return (Decimal)this._ValueInt32;
                    case EValueDataType.Int64: return (Decimal)this._ValueInt64;
                    case EValueDataType.Decimal: return this._ValueDecimal;
                    case EValueDataType.String: return Decimal.Parse(this._ValueString);
                }
                throw new EvalException("Hodnotu typu " + this._Type.ToString() + " nelze konvertovat na Decimal.");
            }
            set
            {
                this._ValueDecimal = value;
                this._Type = EValueDataType.Decimal;
            }
        }
        public DateTime ValueDateTime
        {
            get
            {
                switch (this._Type)
                {
                    case EValueDataType.DateTime: return this._ValueDateTime;
                }
                throw new EvalException("Hodnotu typu " + this._Type.ToString() + " nelze konvertovat na DateTime.");
            }
            set
            {
                this._ValueDateTime = value;
                this._Type = EValueDataType.DateTime;
            }
        }
        public String ValueString
        {
            get
            {
                switch (this._Type)
                {
                    case EValueDataType.Boolean: return this._ValueBoolean.ToString();
                    case EValueDataType.Int32: return this._ValueInt32.ToString();
                    case EValueDataType.Int64: return this._ValueInt64.ToString();
                    case EValueDataType.Decimal: return this._ValueDecimal.ToString();
                    case EValueDataType.String: return this._ValueString;
                }
                throw new EvalException("Hodnotu typu " + this._Type.ToString() + " nelze konvertovat na String.");
            }
            set
            {
                this._ValueString = value;
                this._Type = EValueDataType.String;
            }
        }
        /// <summary>
        /// Aktuální použitý typ v tomto objektu
        /// </summary>
        public EValueDataType Type { get { return this._Type; } }

        public static bool ExactEqual(EValue a, EValue b)
        {
            if (a.Type != b.Type) return false;
            switch (a.Type)
            {
                case EValueDataType.None: return true;
                case EValueDataType.Boolean: return (a._ValueBoolean == b._ValueBoolean);
                case EValueDataType.Int32: return (a._ValueInt32 == b._ValueInt32);
                case EValueDataType.Int64: return (a._ValueInt64 == b._ValueInt64);
                case EValueDataType.Decimal: return (a._ValueDecimal == b._ValueDecimal);
                case EValueDataType.DateTime: return (a._ValueDateTime == b._ValueDateTime);
                case EValueDataType.String: return (a._ValueString == b._ValueString);
            }
            return false;
        }
        #endregion
        #region Private proměnné a enum _DataType
        private Boolean _ValueBoolean;
        private Int32 _ValueInt32;
        private Int64 _ValueInt64;
        private Decimal _ValueDecimal;
        private DateTime _ValueDateTime;
        private String _ValueString;
        private EValueDataType _Type;
        public enum EValueDataType { None = 0, Boolean, Int32, Int64, Decimal, DateTime, String }
        #endregion
    }
    #endregion
    #region class EvalException : výjimka vyvolaná v procesu kompilace nebo vyhodnocení výrazu
    /// <summary>
    /// EvalException : výjimka vyvolaná v procesu kompilace nebo vyhodnocení výrazu
    /// </summary>
    public class EvalException : DevException
    {
        public EvalException(string message)
            : base(message)
        { }
    }
    #endregion
    #region interface IEvaluateContext : deklarace kontextu, který řeší proměnné
    /// <summary>
    /// Interface, díky kterému může funkce požádat o hodnotu proměnné.
    /// Jde typicky o objekt, v jehož instanci se drží řada hodnot (například datum, číslo stránky, aktuální uživatel),
    /// a funkce (když najde mezi svými hodnotami proměnnou) si za pomoci tohoto kontextu získá konkrétní hodnotu.
    /// </summary>
    public interface IEvaluateContext
    {
        /// <summary>
        /// Tato metoda najde a vrátí aktuální hodnotu proměnné
        /// </summary>
        /// <param name="variableName"></param>
        /// <returns></returns>
        EValue GetValueOfVariable(string variableName);
        /// <summary>
        /// Obsahuje (vrátí) soupis všech proměnných, které kontext v dané chvíli může vyhodnotit
        /// </summary>
        IEnumerable<VariableInfo> AllVariableInfo { get; }
    }
    /// <summary>
    /// Informace o všech proměnných
    /// </summary>
    public class VariableInfo
    {
        /// <summary>
        /// Jméno
        /// </summary>
        public string Name { get; set; }
        /// <summary>
        /// Název datového typu
        /// </summary>
        public string TypeName { get; set; }
        /// <summary>
        /// Název skupiny v nabídce, pokud bude zobrazena Categorized
        /// </summary>
        public string ListGroupName { get; set; }
        /// <summary>
        /// ToolTipText k proměnné
        /// </summary>
        public string ToolTipText { get; set; }
    }
    #endregion
    #region interface IExpressionFunction : deklarace pro třídy, které chtějí hrát roli výkonných funkcí při vyhodnocení výrazů
    /// <summary>
    /// Interface IExpressionFunction : deklarace pro třídy, které chtějí hrát roli výkonných funkcí při vyhodnocení výrazů.
    /// Jde o Plugin, který nemá žádné povinné metody, pouze povinnou property FunctionNameCaseSensitive, která je společná všem metodám v třídě.
    /// Reflexí jeho typu se získá souhrn jeho metod, které odpovídají delegátu ExpressionFunctionDelegate = výkonné metody funkcí.
    /// Z atributů těchto funkcí se získají jejich metadata.
    /// </summary>
    public interface IExpressionFunction : IPlugin
    {
        /// <summary>
        /// Deklarace, zda název funkce musí odpovídat exaktně (CaseSensitive = true) nebo bez ohledu na velikost písmen (CaseSensitive = false).
        /// </summary>
        bool FunctionNameCaseSensitive { get; }
    }
    /// <summary>
    /// Delegát, který předepisuje vlastnosti metod, které mohou hrát roli funkcí při vyhodnocování výrazu.
    /// </summary>
    /// <param name="parameterList"></param>
    /// <param name="context"></param>
    /// <returns></returns>
    public delegate EValue ExpressionFunctionDelegate(List<CompiledFormula> parameterList, IEvaluateContext context);
    #endregion
    #region class FunctionImplement : Třída zastřešující každou jednotlivou funkci
    /// <summary>
    /// FunctionImplement : Třída zastřešující každou jednotlivou funkci.
    /// Seznam funkcí se získá reflexí jednotlivých metod daného providera (IExpressionFunction).
    /// Funkci je možno vyvolat standardně metodou Evaluate().
    /// </summary>
    public class FunctionImplement
    {
        #region Konstrukce a public property
        private FunctionImplement(IExpressionFunction provider, System.Reflection.MethodInfo method)
        {
            this._FunctionProvider = provider;
            this._FunctionMethod = method;
            this._ParamCountMode = FunctionParamCountMode.Any;

            // Průzkum atributů:
            object[] attributes = method.GetCustomAttributes(false);
            foreach (object attribute in attributes)
            {
                if (attribute is FunctionNameAttribute)
                    this.Name = (attribute as FunctionNameAttribute).FunctionName;
                if (attribute is FunctionParamHintAttribute)
                    this.ParamHint = (attribute as FunctionParamHintAttribute).FunctionParamHint;
                if (attribute is FunctionParamCountAttribute)
                {
                    this._ParamCountMode = (attribute as FunctionParamCountAttribute).CountMode;
                    this._ParamCounts = (attribute as FunctionParamCountAttribute).ParamCounts;
                }
                if (attribute is FunctionToolTipTextAttribute)
                    this.ToolTipText = (attribute as FunctionToolTipTextAttribute).ToolTipText;
                if (attribute is FunctionListGroupNameAttribute)
                    this.ListGroupName = (attribute as FunctionListGroupNameAttribute).ListGroupName;
            }

            // Klíč jména funkce:
            if (!String.IsNullOrEmpty(this.Name))
                this.Key = (provider.FunctionNameCaseSensitive ? this.Name : this.Name.ToLower());

        }
        public override string ToString()
        {
            return this.Name + "(" + this.ParamHint + ")";
        }
        private IExpressionFunction _FunctionProvider;
        private System.Reflection.MethodInfo _FunctionMethod;
        private FunctionParamCountMode _ParamCountMode;
        private int[] _ParamCounts;
        /// <summary>
        /// Název funkce je Case-sensitive
        /// </summary>
        public bool FunctionNameCaseSensitive { get { return this._FunctionProvider.FunctionNameCaseSensitive; } }
        /// <summary>
        /// Implementační metoda (název typu.název metody)
        /// </summary>
        public string ImplementInMethod { get { return this._FunctionProvider.GetType().Name + "." + this._FunctionMethod.Name; } }
        /// <summary>
        /// Název funkce, pokud není Case-sensitive pak je zde lower()
        /// </summary>
        public string Key { get; private set; }
        /// <summary>
        /// Název funkce včetně parametrů v závorce
        /// </summary>
        public string FullName { get { return this.Name + "(" + (this.ParamHint == null ? "" : this.ParamHint) + ")"; } }
        /// <summary>
        /// Název funkce jak je deklaruje provider
        /// </summary>
        public string Name { get; private set; }
        /// <summary>
        /// Hinty k parametrům
        /// </summary>
        public string ParamHint { get; private set; }
        /// <summary>
        /// Název skupiny v seznamu funkci
        /// </summary>
        public string ListGroupName { get; private set; }
        /// <summary>
        /// Tooltip k funkci
        /// </summary>
        public string ToolTipText { get; private set; }
        #endregion
        #region Static reflexe daného providera, výstup seznamu funkcí
        /// <summary>
        /// Najde a vrátí seznam funkcí, které daný objekt implementuje.
        /// </summary>
        /// <param name="provider"></param>
        /// <returns></returns>
        public static List<FunctionImplement> SearchProvider(IExpressionFunction provider)
        {
            List<FunctionImplement> result = new List<FunctionImplement>();

            System.Reflection.MethodInfo[] methods = provider.GetType().GetMethods(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public);
            foreach (System.Reflection.MethodInfo method in methods)
            {
                // Pokud má metoda návratový typ EValue, a dva parametry (List<CompiledFormula> a IEvaluateContext), pak ji beru do dalšího kola:
                if (_HasMethodCorrectParams(method))
                {
                    FunctionImplement functionImplement = new FunctionImplement(provider, method);
                    if (functionImplement.Key != null)
                        result.Add(functionImplement);
                }
            }

            return result;
        }
        /// <summary>
        /// Zjistí, zda daná metoda odpovídá typem návratové hodnoty a typem dvou parametrů delegátovi ExpressionFunctionDelegate
        /// </summary>
        /// <param name="method"></param>
        /// <returns></returns>
        private static bool _HasMethodCorrectParams(System.Reflection.MethodInfo method)
        {
            if (method.ReturnType != typeof(EValue)) return false;

            System.Reflection.ParameterInfo[] parameters = method.GetParameters();
            if (parameters.Length != 2) return false;

            if (parameters[1].ParameterType != typeof(IEvaluateContext)) return false;

            return true;
        }
        #endregion
        #region Vyhodnocení funkce
        /// <summary>
        /// Zásadní metoda, zde funkce vyhodnotí obsah parametrů a vrátí svoji výstupní hodnotu.
        /// </summary>
        /// <param name="functionName"></param>
        /// <param name="parameterList"></param>
        /// <param name="context"></param>
        /// <returns></returns>
        internal EValue Evaluate(List<CompiledFormula> parameterList, IEvaluateContext context)
        {
            object result = null;
            try
            {
                result = this._FunctionMethod.Invoke(this._FunctionProvider, new object[] { parameterList, context });
            }
            catch (Exception exc)
            {
                throw new EvalException("Ve funkci " + this.FullName + " (" + this.ImplementInMethod + ") došlo k chybě: " + exc.Message);
            }

            if (result is EValue)
                return (EValue)result;
            throw new EvalException("Funkce " + this.ImplementInMethod + " nevrátila očekávanou hodnotu EValue.");
        }
        #endregion
    }
    #endregion
    #region Atributy metod, které popisují funkce
    /// <summary>
    /// Atribut, který specifikuje jméno funkce
    /// </summary>
    public class FunctionNameAttribute : Attribute
    {
        /// <summary>
        /// Konstruktor.
        /// Předává název funkce, bez závorek. Tento název se používá ve výrazech.
        /// </summary>
        /// <param name="functionName"></param>
        public FunctionNameAttribute(string functionName)
        {
            this.FunctionName = functionName;
        }
        /// <summary>
        /// Název funkce, bez závorek. Tento název se používá ve výrazech.
        /// </summary>
        public string FunctionName { get; private set; }
    }
    /// <summary>
    /// Atribut, který specifikuje skupinu v seznamu funkcí (odděleně matematické, textové, datové, atd)
    /// </summary>
    public class FunctionListGroupNameAttribute : Attribute
    {
        /// <summary>
        /// Konstruktor.
        /// </summary>
        /// <param name="listGroupName"></param>
        public FunctionListGroupNameAttribute(string listGroupName)
        {
            this.ListGroupName = listGroupName;
        }
        /// <summary>
        /// Název skupiny v seznamu.
        /// </summary>
        public string ListGroupName { get; private set; }
    }
    /// <summary>
    /// Atribut, který specifikuje popisky parametrů
    /// </summary>
    public class FunctionParamHintAttribute : Attribute
    {
        /// <summary>
        /// Konstruktor.
        /// Předává popisky parametrů, bez závorek. Tyto popisky se uvádějí v seznamu funkcí, v závorkách za jménem funkce.
        /// </summary>
        /// <param name="functionParamHint"></param>
        public FunctionParamHintAttribute(string functionParamHint)
        {
            this.FunctionParamHint = functionParamHint;
        }
        /// <summary>
        /// Název funkce, bez závorek. Tento název se používá ve výrazech.
        /// </summary>
        public string FunctionParamHint { get; private set; }
    }
    /// <summary>
    /// Atribut, který specifikuje nápovědu (ToolTipText) funkce
    /// </summary>
    public class FunctionToolTipTextAttribute : Attribute
    {
        /// <summary>
        /// Konstruktor.
        /// Předává ToolTipText funkce.
        /// </summary>
        /// <param name="toolTipText"></param>
        public FunctionToolTipTextAttribute(string toolTipText)
        {
            this.ToolTipText = toolTipText;
        }
        /// <summary>
        /// ToolTipText funkce.
        /// </summary>
        public string ToolTipText { get; private set; }
    }
    /// <summary>
    /// Atribut, který specifikuje počet parametrů funkce
    /// </summary>
    public class FunctionParamCountAttribute : Attribute
    {
        /// <summary>
        /// Konstruktor.
        /// Předává povolené počty parametrů. Některé funkce mají volitelný počet parametrů, například 2 nebo 3. Pak se zde předají čísla 2, 3.
        /// Pokud tento atribut nebude zadán, předpokládá se počet parametrů libovolný.
        /// </summary>
        /// <param name="functionParamCounts"></param>
        public FunctionParamCountAttribute(params int[] functionParamCounts)
        {
            this.CountMode = FunctionParamCountMode.Exact;
            this.ParamCounts = functionParamCounts;
        }
        /// <summary>
        /// Konstruktor.
        /// Předává povolený režim parametrů.
        /// Pokud tento atribut nebude zadán, předpokládá se počet parametrů libovolný.
        /// </summary>
        /// <param name="countMode"></param>
        public FunctionParamCountAttribute(FunctionParamCountMode countMode)
        {
            this.CountMode = countMode;
            this.ParamCounts = new int[0];
        }
        /// <summary>
        /// Konstruktor.
        /// Předává povolené počty parametrů. Některé funkce mají volitelný počet parametrů, například 2 nebo 3. Pak se zde předají čísla 2, 3.
        /// Pokud tento atribut nebude zadán, předpokládá se počet parametrů libovolný.
        /// </summary>
        /// <param name="countMode"></param>
        /// <param name="count"></param>
        public FunctionParamCountAttribute(FunctionParamCountMode countMode, int count)
        {
            this.CountMode = countMode;
            this.ParamCounts = new int[] {count};
        }
        /// <summary>
        /// Režim parametrů
        /// </summary>
        public FunctionParamCountMode CountMode { get; private set; }
        /// <summary>
        /// Název funkce, bez závorek. Tento název se používá ve výrazech.
        /// </summary>
        public int[] ParamCounts { get; private set; }
    }
    /// <summary>
    /// Specifikuje množství parametrů
    /// </summary>
    public enum FunctionParamCountMode
    {
        /// <summary>
        /// Neurčeno
        /// </summary>
        None,
        /// <summary>
        /// Jakékoli
        /// </summary>
        Any,
        /// <summary>
        /// Přesně daný počet dle uvedeného seznamu
        /// </summary>
        Exact,
        /// <summary>
        /// Nejméně daný počet
        /// </summary>
        Minimum,
        /// <summary>
        /// Nejvíce daný počet
        /// </summary>
        Maximum
    }
    #endregion
    #region Testy
    public class Tests : IEvaluateContext
    {
        public static void Run()
        {
            Tests content = new Tests();

            content.Test("1", 1);
            content.Test("-50.25", -50.25m);
            content.Test("12 + 16", 28);
            content.Test("12 + 16 * 2", 44);
            content.Test("12.25 + 16 / 4", 16.25m);
            content.Test("(12.25 + 16) / 4", 7.0625m);
            content.Test("(12.25 + 16 * -6) / 4", -20.9375m);
            content.Test("45 + 12 * 9 / 3 - 8 / 2", 77);
            content.Test("(45 + 12) * 9 / (3 - 8 / 2)", -513);

            content.Test("len(\"Pokusný králík\")", 14);
            content.Test("len(\"Pokusný králík\" + \" a \" + \"morče.\")", 23);
            content.Test("\"Pokusný králík\" + \" a \" + \"morče.\"", "Pokusný králík a morče.");
            content.Test("len(trim(\"  Pokusný králík \"))", 14);
            content.Test("len(trim(\"  Pokusný králík\" + \" a \" + \"morče.  \")", 23);



            content.TestTime();
        }
        private void Test(string expression, EValue expected)
        {
            EValue result = Evaluator.Evaluate(expression, this);
            if (EValue.ExactEqual(result, expected)) return;
            throw new EvalException("Chyba ve výsledku výrazu. Výraz: " + expression + "; Hodnota: " + result.ToString() + "; Očekávaná: " + expected.ToString());
        }
        private void TestTime()
        {
            int loops = 0;
            System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
            EValue result = EValue.Empty;

            string exp1 = "(45 + 12) * 9 / (3 - 8 / 2)";
            loops = 0;
            result = EValue.Empty;
            sw.Restart();
            while (sw.ElapsedMilliseconds < 100)
            {
                result = Evaluator.Evaluate(exp1, this);
                loops++;
            }
            sw.Stop();
            string res1 = result.ToString();
            float time1 = 100000f / (float)loops;         // 1 vyhodnocení za mikrosekund

            string exp2 = "1 + (2 * 3 + (4 * (5 + 6) - (7 * 8)))";
            loops = 0;
            result = EValue.Empty;
            sw.Restart();
            while (sw.ElapsedMilliseconds < 100)
            {
                result = Evaluator.Evaluate(exp2, this);
                loops++;
            }
            sw.Stop();
            string res2 = result.ToString();
            float time2 = 100000f / (float)loops;         // 1 vyhodnocení za mikrosekund

            if (time1 > 60f)
                throw new EvalException("Chyba v rychlosti vyhodnocení výrazu. Výraz: " + exp1 + "; Čas vyhodnocení: " + time1.ToString("# ##0.00") + " mikrosekund; Očekávaný čas: do 60 mikrosekund.");
            if (time2 > 90f)
                throw new EvalException("Chyba v rychlosti vyhodnocení výrazu. Výraz: " + exp2 + "; Čas vyhodnocení: " + time2.ToString("# ##0.00") + " mikrosekund; Očekávaný čas: do 90 mikrosekund.");

            return;

            System.Windows.Forms.MessageBox.Show("Čas na jedno vyhodnocení výrazu:\r\n" +
                exp1 + " = " + res1 + ";   čas = " + time1.ToString("# ##0.00") + " mikrosekund.\r\n" +
                exp2 + " = " + res2 + ";   čas = " + time2.ToString("# ##0.00") + " mikrosekund.\r\n");
        }
        /// <summary>
        /// Vyhodnocení obsahu proměnné v aktuálním kontextu
        /// </summary>
        /// <param name="variableName"></param>
        /// <returns></returns>
        EValue IEvaluateContext.GetValueOfVariable(string variableName)
        {
            switch (variableName)
            {
                case "variable": return "  Obsah proměnné   ";
                case "seconds": return DateTime.Now.Second;
            }
            return null;
        }
        /// <summary>
        /// Obsahuje (vrátí) soupis všech proměnných, které kontext v dané chvíli může vyhodnotit
        /// </summary>
        IEnumerable<VariableInfo> IEvaluateContext.AllVariableInfo
        {
            get
            {
                List<VariableInfo> list = new List<VariableInfo>();
                list.Add(new VariableInfo() { Name = "variable", TypeName = "String", ListGroupName = "Tests" });
                list.Add(new VariableInfo() { Name = "seconds", TypeName = "Numeric", ListGroupName = "Tests" });
                list.Add(new VariableInfo() { Name = "pagenumber", TypeName = "Numeric", ListGroupName = "Tests" });
                list.Add(new VariableInfo() { Name = "user.name", TypeName = "String", ListGroupName = "User" });
                list.Add(new VariableInfo() { Name = "user.login", TypeName = "String", ListGroupName = "User" });
                list.Add(new VariableInfo() { Name = "user.subject", TypeName = "Numeric", ListGroupName = "User" });
                return list;
            }
        }
    }
    #endregion
}
